package com.powroznik.tracker.engine.services;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;

import com.powroznik.tracker.engine.data.Account;
import com.powroznik.tracker.engine.data.Device;
import com.powroznik.tracker.engine.data.Location;
import com.powroznik.tracker.engine.data.TrackerEngineDao;
import com.powroznik.tracker.engine.data.User;

@Path("/portalService")
public class PortalService {
	private TrackerEngineDao trackerEngineDao;

	private ValidationService validationService;

	@WebMethod
	@POST
	@Path("/connectDevice")
	public void connectDevice(
			@WebParam(name = "account") @FormParam("account") Account account,
			@WebParam(name = "device") @FormParam("device") Device device,
			@WebParam(name = "description") @FormParam("description") String description) {
		assert validationService.isGenuine(account, false);
		assert validationService.isGenuine(device, false);
		assert validationService.isGenuine(description);
		device = trackerEngineDao.getDeviceById(device.getId());
		if (device.getAccount() != null && !device.getAccount().equals(account)) {
			throw new RuntimeException(
					"Device already connected to another account");
		}
		device.setDescription(description);
		device.setAccount(account);
		trackerEngineDao.save(device);
	}

	@WebMethod
	@POST
	@Path("/getAccountByUser")
	@Produces("application/json")
	public Account getAccountByUser(
			@WebParam(name = "user") @QueryParam("user") User user) {
		assert validationService.isGenuine(user);
		user = trackerEngineDao.getUserByKey(user.getKey());
		return user.getAccount();
	}

	@WebMethod
	@GET
	@Path("/getAllDevices")
	@Produces("application/json")
	public List<Device> getAllDevices() {
		return trackerEngineDao.getAllDevices();
	}

	private Location getBestLocation(Collection<Location> locations) {
		Location bestLocation = null;
		for (Location location : locations) {
			if (bestLocation == null) {
				bestLocation = location;
				continue;
			}
			if (location.getAccuracy() != null
					&& bestLocation.getAccuracy() != null) {
				if (location.getAccuracy() < bestLocation.getAccuracy()) {
					bestLocation = location;
					continue;
				}
			}
			if (location.getAccuracy() != null
					&& bestLocation.getAccuracy() == null) {
				bestLocation = location;
				continue;
			}
		}
		return bestLocation;
	}

	@WebMethod
	@GET
	@Path("/getDeviceByCode1")
	@Produces("application/json")
	public Device getDeviceByCode1(
			@WebParam(name = "code1") @QueryParam("code1") String code1) {
		assert validationService.isGenuine(code1);
		return trackerEngineDao.getDeviceByCode1(code1);
	}

	@WebMethod
	@POST
	@Path("/getDevicesByAccount")
	@Produces("application/json")
	public List<Device> getDevicesByAccount(
			@WebParam(name = "account") @FormParam("account") Account account) {
		assert validationService.isGenuine(account);
		return trackerEngineDao.getDevicesByAccount(account);
	}

	@WebMethod
	@POST
	@Path("/getLastKnownLocation")
	@Produces("application/json")
	public Location getLastKnownLocation(
			@WebParam(name = "device") @FormParam("device") Device device) {
		assert validationService.isGenuine(device, false);
		return trackerEngineDao.getLastKnownLocation(device);
	}

	@WebMethod
	@POST
	@Path("/getLastKnownLocations")
	@Produces("application/json")
	public List<Location> getLastKnownLocations(
			@WebParam(name = "device") @FormParam("device") Device device) {
		assert validationService.isGenuine(device, false);
		Map<Long, List<Location>> map = new HashMap<Long, List<Location>>();
		for (Location location : trackerEngineDao.getLastKnownLocations(device)) {
			List<Location> locations = map.get(location.getRun());
			if (locations == null) {
				locations = new ArrayList<Location>();
				map.put(location.getTime().getTime() / (30 * 60 * 1000),
						locations);
			}
			locations.add(location);
		}
		List<Location> locations = new ArrayList<Location>();
		for (Long run : map.keySet()) {
			locations.add(getBestLocation(map.get(run)));
		}
		Collections.sort(locations, new Comparator<Location>() {
			@Override
			public int compare(Location o1, Location o2) {
				return o1.getTime().compareTo(o2.getTime());
			}
		});
		return locations;
	}

	@WebMethod
	@GET
	@Path("/getUserByKey")
	@Produces("application/json")
	public User getUserByKey(
			@WebParam(name = "key") @QueryParam("key") String key) {
		assert validationService.isGenuine(key);
		User user = trackerEngineDao.getUserByKey(key);
		if (user == null) {
			user = new User();
			user.setKey(key);
			trackerEngineDao.save(user);
		}
		return user;
	}

	@WebMethod
	@GET
	@Path("/ping")
	@Produces("text/plain")
	public String ping(
			@WebParam(name = "message") @QueryParam("message") String message) {
		assert validationService.isGenuine(message);
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < message.length(); i++) {
			sb.insert(0, message.charAt(i));
		}
		return sb.toString();
	}

	@WebMethod
	@POST
	@Path("/registerNewAccount")
	public void registerNewAccount(
			@WebParam(name = "user") @FormParam("user") User user,
			@WebParam(name = "account") @FormParam("account") Account account) {
		assert validationService.isGenuine(account, false);
		assert validationService.isGenuine(user, false);
		trackerEngineDao.save(account);
		user.setAccount(account);
		trackerEngineDao.save(user);
	}

	public void setTrackerEngineDao(TrackerEngineDao trackerEngineDao) {
		this.trackerEngineDao = trackerEngineDao;
	}

	public void setValidationService(ValidationService validationService) {
		this.validationService = validationService;
	}

	@WebMethod
	@POST
	@Path("/updateAccount")
	public void updateAccount(
			@WebParam(name = "user") @FormParam("user") User user,
			@WebParam(name = "account") @FormParam("account") Account account) {
		assert validationService.isGenuine(account, false);
		assert validationService.isGenuine(user, false);
		trackerEngineDao.save(account);
	}
}
